package service

import (
	"context"
	"gitee.com/crack007/goose"
	"github.com/crack007/go-im/pb"
	"github.com/crack007/go-im/src/common/model"
	"google.golang.org/grpc"
	"strconv"
	"sync"
)

// 处理sentinel之间的通信
type grpcService struct {
	lock sync.Mutex
}

func NewGrpcService() *grpcService {
	return &grpcService{}
}

func (g *grpcService) NodeStatusAsk(ctx context.Context, nodeInfo *pb.NodeInfo) (*pb.NodeStatus, error) {
	// 立即发送ping检测
	online := SentinelConnection.TestNode(nodeInfo.Id)
	if !online {
		SentinelService.SetNodeOffline(nodeInfo.Id)
	}
	return &pb.NodeStatus{Online: online}, nil
}

func (g *grpcService) SentinelStatusAsk(ctx context.Context, sentinelInfo *pb.SentinelInfo) (*pb.NodeStatus, error) {
	// 立即发送ping检测
	online := SentinelConnection.TestSentinel(sentinelInfo.Id)
	return &pb.NodeStatus{Online: online}, nil
}

// 来自其他sentinel节点的广播消息处理
func (g *grpcService) SentinelOnline(ctx context.Context, sentinelInfo *pb.SentinelInfo) (*pb.SentinelInfo, error) {
	goose.GetLogger().Debug("BroadcastSentinelOnline-> id:%s ip:%s port:%d", sentinelInfo.Id, sentinelInfo.Ip, sentinelInfo.Port)
	sentinel := model.GetSentinelInfo(SentinelService.CurrentNode)

	//  记录其他sentinel节点信息
	if SentinelService.IsSentinelExist(sentinelInfo.Id) {
		return sentinel, nil
	}
	// 和其他sentinel节点打通
	sentinelModel := model.NewSentinelModel()
	sentinelModel.Id = sentinel.Id
	sentinelModel.Ip = sentinel.Ip
	sentinelModel.Port = uint16(sentinel.Port)
	sentinelModel.Epoch = sentinel.Epoch
	// 和目标节点打通连接

	err := SentinelConnection.ConnectSentinel(sentinelModel, SentinelService.CurrentNode)
	if err != nil {
		return nil, err
	}
	return sentinel, nil
}

// 来自node节点的广播消息处理
func (g *grpcService) WorkerBroadcast(ctx context.Context, masterBroadcast *pb.NodeBroadcastReq) (*pb.Resp, error) {
	//   记录slave节点和master节点
	sourceNode := masterBroadcast.SourceNode
	goose.GetLogger().Debug("BroadcastFromNode-> id:%s", sourceNode.Id)
	g.lock.Lock()
	defer g.lock.Unlock()
	// 处理node节点
	for _, nodeInfo := range masterBroadcast.NodeMapInfo.NodeList {
		if SentinelService.IsNodeExist(nodeInfo.Id) {
			continue
		}
		goose.GetLogger().Debug("Node online-> id:%s", nodeInfo.Id)
		nodeModel := model.GetWorkModel(nodeInfo)
		err := SentinelConnection.ConnectNode(nodeModel, SentinelService.CurrentNode)
		if err != nil {
			return nil, err
		}
		SentinelService.AddNode(nodeModel)
	}
	// 处理sentinel节点
	for _, sentinel := range masterBroadcast.NodeMapInfo.SentinelList {
		if SentinelService.IsSentinelExist(sentinel.Id) {
			continue
		}
		// 和其他sentinel节点打通
		sentinelModel := model.NewSentinelModel()
		sentinelModel.Id = sentinel.Id
		sentinelModel.Ip = sentinel.Ip
		sentinelModel.Epoch = sentinel.Epoch
		sentinelModel.Port = uint16(sentinel.Port)
		// 和目标节点打通连接
		target := sentinelModel.Ip + ":" + strconv.Itoa(int(sentinelModel.Port))
		client, err := grpc.Dial(target, grpc.WithInsecure(), grpc.WithBlock())
		if err != nil {
			return nil, err
		}
		sentinelModel.Client = pb.NewSentinelServiceClient(client)
		SentinelService.AddSentinelNode(sentinelModel)
	}
	return &pb.Resp{}, nil
}
func (g *grpcService) Ping(ctx context.Context, pingReq *pb.PingReq) (*pb.PongResp, error) {
	return &pb.PongResp{SourceId: SentinelService.CurrentNode.Id}, nil
}

// 处理投票请求
func (g *grpcService) Vote(ctx context.Context, voteReq *pb.VoteReq) (*pb.VoteResp, error) {
	voteResp := &pb.VoteResp{}
	voteModel := model.NewVoteModel()
	voteModel.LeaderId = voteReq.LeaderId
	voteModel.Epoch = voteReq.Epoch
	voteResult := <-SentinelService.PushVote(voteModel)
	goose.GetLogger().Debug("VoteResult Epoch:%d LeaderId:%s", voteResult.Epoch, voteResult.LeaderId)

	voteResp.Epoch = voteResult.Epoch
	voteResp.LeaderId = voteResult.LeaderId
	return voteResp, nil
}

// leader节点通知下线的work节点
func (g *grpcService) WorkerOffline(ctx context.Context, nodeInfo *pb.NodeInfo) (*pb.Resp, error) {
	goose.GetLogger().Debug("WorkerOffline id:%s", nodeInfo.Id)
	SentinelService.RemoveWork(nodeInfo.Id)
	return &pb.Resp{}, nil
}

// 更新worker节点信息
func (g *grpcService) WorkerUpdate(ctx context.Context, nodeInfo *pb.NodeInfo) (*pb.Resp, error) {
	goose.GetLogger().Debug("WorkerUpdate id:%s", nodeInfo.Id)
	SentinelService.UpdateWork(model.GetWorkModel(nodeInfo))
	return &pb.Resp{}, nil
}
